package com.guocloud.medimind.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.guocloud.medimind.exception.TemplateProcessException;
import com.guocloud.medimind.service.TemplateProcessService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.api.Advisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.Map;

@Slf4j
@Service
public class TemplateProcessServiceImpl implements TemplateProcessService {

    @Autowired
    private ChatClient chatClient;
    
    @Autowired
    private ObjectMapper objectMapper;
    
    @Autowired
    private Advisor loveAppRagCloudAdvisor;

    @Override
    public Map<String, Object> extractKeysFromText(String textContent) {
        if (!StringUtils.hasText(textContent)) {
            throw new TemplateProcessException("文本内容不能为空");
        }
        
        log.info("开始从文本中提取键结构");
        
        String systemPrompt = """
            你是一个医疗文档结构化专家，擅长分析医疗文档并提取其中的结构。
            请将给定的医疗文档文本转换为JSON格式的结构。
            
            严格遵循以下规则：
            1. 只生成JSON键，不要填充值（除了病历名称外）
            2. 对于病历名称，请提供实际值作为"medical record template"字段
            3. 根据文档内容组织合理的层次结构
            4. 不要包含实际的内容值，所有字段值应为空字符串
            5. 只输出JSON，不要有任何其他解释文本
            6. 确保生成的JSON格式正确，可以被解析
            
            例如，对于输入文本：
            ```
            入院记录
            姓名：张三 性别：男 年龄：45岁
            主诉：头痛3天
            现病史：患者3天前无明显诱因出现头痛...
            ```
            
            你应该输出：
            ```json
            {
                "medical record template": "入院记录",
                "patient_info": {
                    "name": "",
                    "gender": "",
                    "age": ""
                },
                "medical_history": {
                    "chief_complaint": "",
                    "present_illness": ""
                }
            }
            ```
            
            只输出JSON格式，不要包含任何其他文本。
            """;
        
        try {
            String text = chatClient.prompt()
                    .system(systemPrompt)
                    .user(textContent)
                    .call()
                    .chatResponse().getResult().getOutput().getText();
            // 清理响应，确保是有效的JSON
            String jsonResponse = cleanJsonResponse(text);
            
            // 解析JSON
            try {
                @SuppressWarnings("unchecked")
                Map<String, Object> result = objectMapper.readValue(jsonResponse, Map.class);
                
                // 确保包含病历模板名称
                if (!result.containsKey("medical record template")) {
                    result.put("medical record template", "未知病历类型");
                }
                
                log.info("成功提取键结构: {}", result.keySet());
                return result;
                
            } catch (JsonProcessingException e) {
                log.error("解析AI返回的JSON失败", e);
                throw new TemplateProcessException("解析AI返回的结构失败：" + e.getMessage());
            }
            
        } catch (Exception e) {
            log.error("调用AI提取键结构失败", e);
            throw new TemplateProcessException("调用AI提取键结构失败：" + e.getMessage());
        }
    }

    @Override
    public Map<String, Object> matchTemplate(Map<String, Object> keysJson) {
        if (keysJson == null || keysJson.isEmpty()) {
            throw new TemplateProcessException("键结构不能为空");
        }
        
        log.info("开始匹配模板");
        
        try {
            // 获取病历模板名称
            String templateName = (String) keysJson.getOrDefault("medical record template", "未知病历类型");
            log.info("病历类型: {}", templateName);
            
            // 将键结构转换为JSON字符串
            String keysJsonStr = objectMapper.writeValueAsString(keysJson);
            
            // 使用RAG从知识库获取匹配的模板
            String ragQuery = String.format(
                "我需要知识库中「%s」的病历模板JSON结构。请直接返回知识库中存储的原始模板，不要进行任何修改或调整。不需要考虑我提供的键结构：%s。如果找到了「%s」模板，请完全按照知识库中的原始JSON结构返回，不要添加任何解释或修改任何字段。如果知识库中确实没有这个模板，只有这种情况下才根据键结构创建新模板。", 
                templateName, keysJsonStr, templateName
            );
            
            log.info("RAG查询: {}", ragQuery);
            
            // 使用RAG增强的查询
            String systemPrompt = """
                你是一个医疗模板检索专家。你的任务是从知识库中找到精确匹配的模板并原样返回，不要进行任何修改。
                
                规则：
                1. 只输出JSON格式的模板，不要包含任何其他解释文本
                2. 如果在知识库中找到了请求的模板，必须完全按照知识库中的格式返回
                3. 不要基于用户提供的键结构修改模板
                4. 如果知识库中没有请求的模板，才可以创建新模板
                
                示例输出格式：
                {
                  "medical record template": "模板名称",
                  "field1": "",
                  "field2": {
                    "subfield": ""
                  }
                }
                """;
                
            String rawResponse = chatClient.prompt()
                .system(systemPrompt)
                .user(ragQuery)
                .advisors(loveAppRagCloudAdvisor) // 使用RAG advisor进行增强
                .call()
                .chatResponse().getResult().getOutput().getText();
            
            log.info("RAG原始响应: {}", rawResponse);
            
            // 清理响应，确保是有效的JSON
            String templateJson = cleanJsonResponse(rawResponse);
            
            log.info("RAG返回的模板(清理后): {}", templateJson);
            
            // 解析返回的JSON模板
            try {
                @SuppressWarnings("unchecked")
                Map<String, Object> matchedTemplate = objectMapper.readValue(templateJson, Map.class);
                
                // 确保模板包含病历类型
                if (!matchedTemplate.containsKey("medical record template")) {
                    matchedTemplate.put("medical record template", templateName);
                }
                
                log.info("成功匹配模板: {}", templateName);
                return matchedTemplate;
                
            } catch (JsonProcessingException e) {
                log.error("解析RAG返回的模板JSON失败", e);
                
                // 如果解析失败，回退到原始键结构
                log.info("解析失败，使用输入的键结构作为模板");
                return keysJson;
            }
            
        } catch (Exception e) {
            log.error("匹配模板失败", e);
            // 发生异常时，回退到原始键结构
            return keysJson;
        }
    }

    @Override
    public Map<String, Object> generateFullRecord(String originalText, Map<String, Object> matchedTemplate) {
        if (!StringUtils.hasText(originalText)) {
            throw new TemplateProcessException("原始文本不能为空");
        }
        
        if (matchedTemplate == null || matchedTemplate.isEmpty()) {
            throw new TemplateProcessException("匹配模板不能为空");
        }
        
        log.info("开始生成完整病历记录");
        
        try {
            // 将模板转换为JSON字符串，供AI使用
            String templateJson = objectMapper.writeValueAsString(matchedTemplate);
            
            String systemPrompt = """
                你是一个医疗文档结构化专家，擅长从非结构化文本中提取信息并填充到结构化模板中。
                
                请根据提供的原始医疗文档文本和结构化模板，生成完整的结构化JSON文档。
                
                严格遵循以下规则：
                1. 使用提供的模板结构，模板中每一个字段都应该输出
                2. 从原始文本中提取相关信息填充到模板中
                3. 保持模板的层次结构不变
                4. 如果原始文本中没有特定字段的信息，将该字段设置为空字符串
                5. 只输出JSON，不要有任何其他解释文本
                6. 确保生成的JSON格式正确，可以被解析
                
                模板结构如下：
                %s
                
                请分析原始文本并将提取的信息填充到这个模板中。
                只输出最终的JSON结果，不要包含任何其他文本。
                """.formatted(templateJson);
            
            String text = chatClient.prompt()
                    .system(systemPrompt)
                    .user(originalText)
                    // 这里也可以使用RAG进行增强，但在这个步骤可能不需要
                    // .advisors(ragAdvisor)
                    .call()
                    .chatResponse().getResult().getOutput().getText();
            
            // 清理响应，确保是有效的JSON
            String jsonResponse = cleanJsonResponse(text);
            
            // 解析JSON
            try {
                @SuppressWarnings("unchecked")
                Map<String, Object> result = objectMapper.readValue(jsonResponse, Map.class);
                log.info("成功生成完整病历记录");
                return result;
                
            } catch (JsonProcessingException e) {
                log.error("解析AI返回的JSON失败", e);
                throw new TemplateProcessException("解析AI返回的结构失败：" + e.getMessage());
            }
            
        } catch (Exception e) {
            log.error("生成完整病历记录失败", e);
            throw new TemplateProcessException("生成完整病历记录失败：" + e.getMessage());
        }
    }
    
    @Override
    public Map<String, Object> processSingleStep(String textContent) {
        if (!StringUtils.hasText(textContent)) {
            throw new TemplateProcessException("文本内容不能为空");
        }
        
        log.info("开始一站式处理病历文本");
        
        try {
            String systemPrompt = """
                你是一个医疗文档结构化专家，擅长从非结构化医疗文本中提取信息并转换为结构化JSON格式。
                
                请使用以下步骤处理医疗文档：
                
                1. 分析输入的医疗文档文本，确定文档类型（如入院记录、病程记录、手术记录等）
                2. 从你的知识库中查找对应类型的病历模板结构
                3. 按照找到的模板结构，从原始文本中提取相关信息并填充
                
                严格遵循以下规则：
                1. 必须先确定文档类型，再查找对应模板
                2. 使用知识库中的模板结构（如果存在）
                3. 如果知识库中没有完全匹配的模板，根据文档内容创建合适的结构
                4. 从原始文本中提取所有相关信息填充到模板中
                5. 保持模板的层次结构不变
                6. 如果原始文本中没有特定字段的信息，将该字段设置为空字符串
                7. 只输出JSON，不要有任何其他解释文本
                8. 确保生成的JSON格式正确，可以被解析
                9. JSON中应包含"medical record template"字段，表明病历类型
                
                请分析原始文本并生成完整的结构化JSON。
                只输出最终的JSON结果，不要包含任何其他文本。
                """;
            
            log.info("使用RAG查询知识库中的模板");
            
            String text = chatClient.prompt()
                    .system(systemPrompt)
                    .user(textContent)
                    .advisors(loveAppRagCloudAdvisor) // 使用RAG advisor进行增强
                    .call()
                    .chatResponse().getResult().getOutput().getText();
            
            log.info("AI原始响应: {}", text);
            
            // 清理响应，确保是有效的JSON
            String jsonResponse = cleanJsonResponse(text);
            
            log.info("清理后的JSON: {}", jsonResponse);
            
            // 解析JSON
            try {
                @SuppressWarnings("unchecked")
                Map<String, Object> result = objectMapper.readValue(jsonResponse, Map.class);
                
                // 确保包含病历模板名称
                if (!result.containsKey("medical record template")) {
                    result.put("medical record template", "未知病历类型");
                }
                
                log.info("一站式处理完成，成功生成结构化病历记录");
                return result;
                
            } catch (JsonProcessingException e) {
                log.error("解析AI返回的JSON失败", e);
                
                // 尝试修复JSON格式
                String fixedJson = tryFixJson(jsonResponse);
                try {
                    @SuppressWarnings("unchecked")
                    Map<String, Object> result = objectMapper.readValue(fixedJson, Map.class);
                    log.info("通过修复JSON格式成功解析响应");
                    return result;
                } catch (JsonProcessingException ex) {
                    log.error("修复JSON后仍然解析失败", ex);
                    throw new TemplateProcessException("解析AI返回的结构失败：" + e.getMessage());
                }
            }
            
        } catch (Exception e) {
            log.error("一站式处理病历文本失败", e);
            throw new TemplateProcessException("一站式处理病历文本失败：" + e.getMessage());
        }
    }
    
    // 清理AI返回的JSON响应，去掉可能包含的代码块标记等
    private String cleanJsonResponse(String jsonResponse) {
        if (jsonResponse == null) {
            return "{}";
        }
        
        // 移除可能存在的Markdown代码块标记
        jsonResponse = jsonResponse.replaceAll("```json", "")
                .replaceAll("```", "")
                .trim();
        
        // 查找第一个JSON对象的开始和结束位置
        int jsonStart = jsonResponse.indexOf("{");
        int jsonEnd = jsonResponse.lastIndexOf("}") + 1;
        
        if (jsonStart >= 0 && jsonEnd > jsonStart) {
            // 提取JSON部分
            jsonResponse = jsonResponse.substring(jsonStart, jsonEnd);
        }
        
        return jsonResponse;
    }
    
    /**
     * 尝试修复不完整的JSON
     */
    private String tryFixJson(String brokenJson) {
        try {
            // 检查是否缺少结束括号
            int openBraces = countChar(brokenJson, '{');
            int closeBraces = countChar(brokenJson, '}');
            int openQuotes = countChar(brokenJson, '"');
            
            log.info("修复前 - 开括号: {}, 闭括号: {}, 引号: {}", openBraces, closeBraces, openQuotes);
            
            StringBuilder sb = new StringBuilder(brokenJson);
            
            // 检查括号是否配对
            if (openBraces > closeBraces) {
                // 添加缺少的结束括号
                for (int i = 0; i < openBraces - closeBraces; i++) {
                    sb.append("}");
                }
                log.info("已添加 {} 个缺失的结束括号", openBraces - closeBraces);
            }
            
            // 检查引号是否成对
            if (openQuotes % 2 != 0) {
                // 如果引号数量不是偶数，可能缺少一个结束引号
                sb.append("\"");
                log.info("已添加一个缺失的引号");
            }
            
            return sb.toString();
        } catch (Exception e) {
            log.error("无法修复JSON: {}", e.getMessage());
            
            // 如果修复失败，返回原JSON
            return brokenJson;
        }
    }
    
    /**
     * 计算字符在字符串中出现的次数
     */
    private int countChar(String str, char ch) {
        int count = 0;
        for (int i = 0; i < str.length(); i++) {
            if (str.charAt(i) == ch) {
                count++;
            }
        }
        return count;
    }
} 